<?php
/*
 * ntlm_sasl_client.php @(#) $Id: ntlm_sasl_client.php,v 1.3 2004/11/17 08:00:37
 * mlemos Exp $ * * Source: http://www.phpclasses.org/browse/file/7495.html *
 * License: BSD
 * (http://www.phpclasses.org/package/1888-PHP-Single-API-for-standard-authentication-mechanisms.html)
 * * Bundled with Permission *
 */

define ( "SASL_NTLM_STATE_START", 0 );
define ( "SASL_NTLM_STATE_IDENTIFY_DOMAIN", 1 );
define ( "SASL_NTLM_STATE_RESPOND_CHALLENGE", 2 );
define ( "SASL_NTLM_STATE_DONE", 3 );
class ntlm_sasl_client_class {
	var $credentials = array ();
	var $state = SASL_NTLM_STATE_START;
	Function Initialize(&$client) {
		if (! function_exists ( $function = "mcrypt_encrypt" ) || ! function_exists ( $function = "mhash" )) {
			$extensions = array (
					"mcrypt_encrypt" => "mcrypt",
					"mhash" => "mhash" 
			);
			$client->error = "the extension " . $extensions [$function] . " required by the NTLM SASL client class is not available in this PHP configuration";
			return (0);
		}
		return (1);
	}
	Function ASCIIToUnicode($ascii) {
		for($unicode = "", $a = 0; $a < strlen ( $ascii ); $a ++)
			$unicode .= substr ( $ascii, $a, 1 ) . chr ( 0 );
		return ($unicode);
	}
	Function TypeMsg1($domain, $workstation) {
		$domain_length = strlen ( $domain );
		$workstation_length = strlen ( $workstation );
		$workstation_offset = 32;
		$domain_offset = $workstation_offset + $workstation_length;
		return ("NTLMSSP\0" . "\x01\x00\x00\x00" . "\x07\x32\x00\x00" . pack ( "v", $domain_length ) . pack ( "v", $domain_length ) . pack ( "V", $domain_offset ) . pack ( "v", $workstation_length ) . pack ( "v", $workstation_length ) . pack ( "V", $workstation_offset ) . $workstation . $domain);
	}
	Function NTLMResponse($challenge, $password) {
		$unicode = $this->ASCIIToUnicode ( $password );
		$md4 = mhash ( MHASH_MD4, $unicode );
		$padded = $md4 . str_repeat ( chr ( 0 ), 21 - strlen ( $md4 ) );
		$iv_size = mcrypt_get_iv_size ( MCRYPT_DES, MCRYPT_MODE_ECB );
		$iv = mcrypt_create_iv ( $iv_size, MCRYPT_RAND );
		for($response = "", $third = 0; $third < 21; $third += 7) {
			for($packed = "", $p = $third; $p < $third + 7; $p ++)
				$packed .= str_pad ( decbin ( ord ( substr ( $padded, $p, 1 ) ) ), 8, "0", STR_PAD_LEFT );
			for($key = "", $p = 0; $p < strlen ( $packed ); $p += 7) {
				$s = substr ( $packed, $p, 7 );
				$b = $s . ((substr_count ( $s, "1" ) % 2) ? "0" : "1");
				$key .= chr ( bindec ( $b ) );
			}
			$ciphertext = mcrypt_encrypt ( MCRYPT_DES, $key, $challenge, MCRYPT_MODE_ECB, $iv );
			$response .= $ciphertext;
		}
		return $response;
	}
	Function TypeMsg3($ntlm_response, $user, $domain, $workstation) {
		$domain_unicode = $this->ASCIIToUnicode ( $domain );
		$domain_length = strlen ( $domain_unicode );
		$domain_offset = 64;
		$user_unicode = $this->ASCIIToUnicode ( $user );
		$user_length = strlen ( $user_unicode );
		$user_offset = $domain_offset + $domain_length;
		$workstation_unicode = $this->ASCIIToUnicode ( $workstation );
		$workstation_length = strlen ( $workstation_unicode );
		$workstation_offset = $user_offset + $user_length;
		$lm = "";
		$lm_length = strlen ( $lm );
		$lm_offset = $workstation_offset + $workstation_length;
		$ntlm = $ntlm_response;
		$ntlm_length = strlen ( $ntlm );
		$ntlm_offset = $lm_offset + $lm_length;
		$session = "";
		$session_length = strlen ( $session );
		$session_offset = $ntlm_offset + $ntlm_length;
		return ("NTLMSSP\0" . "\x03\x00\x00\x00" . pack ( "v", $lm_length ) . pack ( "v", $lm_length ) . pack ( "V", $lm_offset ) . pack ( "v", $ntlm_length ) . pack ( "v", $ntlm_length ) . pack ( "V", $ntlm_offset ) . pack ( "v", $domain_length ) . pack ( "v", $domain_length ) . pack ( "V", $domain_offset ) . pack ( "v", $user_length ) . pack ( "v", $user_length ) . pack ( "V", $user_offset ) . pack ( "v", $workstation_length ) . pack ( "v", $workstation_length ) . pack ( "V", $workstation_offset ) . pack ( "v", $session_length ) . pack ( "v", $session_length ) . pack ( "V", $session_offset ) . "\x01\x02\x00\x00" . $domain_unicode . $user_unicode . $workstation_unicode . $lm . $ntlm);
	}
	Function Start(&$client, &$message, &$interactions) {
		if ($this->state != SASL_NTLM_STATE_START) {
			$client->error = "NTLM authentication state is not at the start";
			return (SASL_FAIL);
		}
		$this->credentials = array (
				"user" => "",
				"password" => "",
				"realm" => "",
				"workstation" => "" 
		);
		$defaults = array ();
		$status = $client->GetCredentials ( $this->credentials, $defaults, $interactions );
		if ($status == SASL_CONTINUE)
			$this->state = SASL_NTLM_STATE_IDENTIFY_DOMAIN;
		Unset ( $message );
		return ($status);
	}
	Function Step(&$client, $response, &$message, &$interactions) {
		switch ($this->state) {
			case SASL_NTLM_STATE_IDENTIFY_DOMAIN :
				$message = $this->TypeMsg1 ( $this->credentials ["realm"], $this->credentials ["workstation"] );
				$this->state = SASL_NTLM_STATE_RESPOND_CHALLENGE;
				break;
			case SASL_NTLM_STATE_RESPOND_CHALLENGE :
				$ntlm_response = $this->NTLMResponse ( substr ( $response, 24, 8 ), $this->credentials ["password"] );
				$message = $this->TypeMsg3 ( $ntlm_response, $this->credentials ["user"], $this->credentials ["realm"], $this->credentials ["workstation"] );
				$this->state = SASL_NTLM_STATE_DONE;
				break;
			case SASL_NTLM_STATE_DONE :
				$client->error = "NTLM authentication was finished without success";
				return (SASL_FAIL);
			default :
				$client->error = "invalid NTLM authentication step state";
				return (SASL_FAIL);
		}
		return (SASL_CONTINUE);
	}
}
;

?>